﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;

namespace Framework.Components
{


    /// <summary>
    /// 日志
    /// </summary>
    public static class Log
    {
        private static Logger instance = new Logger();

        /// <summary>
        /// 静态日志对象
        /// </summary>
        public static Logger Instance { get { return instance; } }

        [Conditional("DEBUG")]
        public static void Debug(string msg)
        {
            instance.WriteLog(LogType.DEBUG, msg, 4, false);
        }

        [Conditional("DEBUG")]
        public static void Debug(string msg, bool stackTrace)
        {
            instance.WriteLog(LogType.DEBUG, msg, 4, stackTrace);
        }

        [Conditional("TRACE")]
        public static void Info(string msg)
        {
            instance.WriteLog(LogType.INFO, msg, 4, false);
        }

        [Conditional("TRACE")]
        public static void Info(string msg, bool stackTrace)
        {
            instance.WriteLog(LogType.INFO, msg, 4, stackTrace);
        }

        [Conditional("TRACE")]
        public static void Warn(string msg)
        {
            instance.WriteLog(LogType.WARN, msg, 4, false);
        }

        [Conditional("TRACE")]
        public static void Warn(string msg, bool stackTrace)
        {
            instance.WriteLog(LogType.WARN, msg, 4, stackTrace);
        }

        [Conditional("TRACE")]
        public static void Error(string msg)
        {
            instance.WriteLog(LogType.ERROR, msg, 4, false);
        }

        [Conditional("TRACE")]
        public static void Error(string msg, Exception e)
        {
            instance.WriteLog(LogType.ERROR, msg, 4, e);
        }

        [Conditional("TRACE")]
        public static void Error(string msg, bool stackTrace)
        {
            instance.WriteLog(LogType.ERROR, msg, 4, stackTrace);
        }

        [Conditional("TRACE")]
        public static void Write(LogType type, string msg, int funcLevel, bool stackTrace)
        {
            instance.WriteLog(type, msg, funcLevel, stackTrace);
        }

        /// <summary>
        /// 日志对象(单独)
        /// </summary>
        public class Logger
        {
            private ILogHandler handler = new ConsoleLogHandler();

            /// <summary>
            /// 日志处理接口
            /// </summary>
            public ILogHandler Handler { get { return handler; } set { handler = value; } }

            [Conditional("DEBUG")]
            public void Debug(string msg)
            {
                WriteLog(LogType.DEBUG, msg, 4, false);
            }

            [Conditional("DEBUG")]
            public void Debug(string msg, bool stackTrace)
            {
                WriteLog(LogType.DEBUG, msg, 4, stackTrace);
            }

            [Conditional("TRACE")]
            public void Info(string msg)
            {
                WriteLog(LogType.INFO, msg, 4, false);
            }

            [Conditional("TRACE")]
            public void Info(string msg, bool stackTrace)
            {
                WriteLog(LogType.INFO, msg, 4, stackTrace);
            }

            [Conditional("TRACE")]
            public void Warn(string msg)
            {
                WriteLog(LogType.WARN, msg, 4, false);
            }

            [Conditional("TRACE")]
            public void Warn(string msg, bool stackTrace)
            {
                WriteLog(LogType.WARN, msg, 4, stackTrace);
            }

            [Conditional("TRACE")]
            public void Error(string msg)
            {
                WriteLog(LogType.ERROR, msg, 4, false);
            }

            [Conditional("TRACE")]
            public void Error(string msg, Exception e)
            {
                WriteLog(LogType.ERROR, msg, 4, e);
            }

            [Conditional("TRACE")]
            public void Error(string msg, bool stackTrace)
            {
                WriteLog(LogType.ERROR, msg, 4, stackTrace);
            }

            [Conditional("TRACE")]
            public void Write(LogType type, string msg, int funcLevel, bool stackTrace)
            {
                WriteLog(type, msg, funcLevel, stackTrace);
            }

            /// <summary>
            /// 写入日志
            /// </summary>
            internal void WriteLog(LogType type, string msg, int funcLevel, bool stackTrace)
            {
                if (handler == null)
                {
                    Console.WriteLine("no log handler !");
                    return;
                }
                //handler.WriteLog(type, GetStackTraceFuncString(funcLevel), (stackTrace) ? GetStackTraceString(funcLevel-1, 0) : "", msg);
                handler.WriteLog(type, GetStackTraceFuncString(funcLevel), (stackTrace) ? GetStackTraceString(funcLevel, 0) : "", msg);
            }

            /// <summary>
            /// 写入日志
            /// </summary>
            internal void WriteLog(LogType type, string msg, int funcLevel, Exception e)
            {
                if (handler == null)
                {
                    Console.WriteLine("no log handler !");
                    return;
                }
                handler.WriteLog(type, GetStackTraceFuncString(funcLevel), e.ToString() + e.StackTrace, msg);
            }


            /// <summary>
            /// 获取堆栈数据
            /// </summary>
            internal static System.Diagnostics.StackFrame GetStackTrace(int level)
            {
                //当前堆栈信息
                var st = new System.Diagnostics.StackTrace(true);
                var sfs = st.GetFrames();
                if (level < 0 || level >= sfs.Length) { return null; }
                return sfs[level];
            }

            /// <summary>
            /// 获取堆栈字符串
            /// </summary>
            /// <param name="level"></param>
            /// <returns></returns>
            internal static string GetStackTraceString(int level, int depth)
            {
                var st = new System.Diagnostics.StackTrace(true);
                var sfs = st.GetFrames();
                if (level < 0 || level >= sfs.Length) { return null; }
                depth = (depth != 0) ? depth : sfs.Length - level;
                var strBdr = new StringBuilder();
                // 遍历输出
                //strBdr.Append(sfs[level].ToString());
                for (int i = 0; i < depth; i++)
                {
                    if (i > 0) { strBdr.Append("\r\n"); }
                    var sf = sfs[level + i];
                    strBdr.Append("\t");
                    // strBdr.Append(sf.ToString());
                    strBdr.Append(" at ");
                    strBdr.Append((sf.GetMethod() != null) ? sf.GetMethod().Name : "UnKnown");
                    strBdr.Append("(");
                    strBdr.Append(sf.GetFileName());
                    strBdr.Append(":");
                    strBdr.Append(sf.GetFileLineNumber());
                    strBdr.Append(")");
                }
                return strBdr.ToString();
            }

            /// <summary>
            /// 读取堆栈函数和行数
            /// </summary>
            /// <param name="level"></param>
            /// <returns></returns>
            internal static string GetStackTraceFuncString(int level)
            {
                //StackTrace st = new StackTrace(new StackFrame(true));
                //StackFrame stackFrame = st.GetFrame(level);
                StackFrame stackFrame = GetStackTrace(level);
                if (stackFrame == null) { return ""; }
                // 文件名
                ///Warn Directory.GetCurrentDirectory(); // 当前程序工作目录
                string fileName = Path.GetFileName(stackFrame.GetFileName());

                // 输出
                StringBuilder strBdr = new StringBuilder();
                // strBdr.Append(stackFrame.GetMethod().ToString());
                strBdr.Append(stackFrame.GetMethod().Name);
                strBdr.Append("(");
                strBdr.Append(fileName);
                strBdr.Append(":");
                strBdr.Append(stackFrame.GetFileLineNumber());
                strBdr.Append(")");
                return strBdr.ToString();
            }


            /// <summary>
            /// 通用转化字符串
            /// </summary>
            internal static string ToString(LogType type, DateTime time, string threadName, string funcName, string stackTraceStr, string msg)
            {
                StringBuilder strBdr = new StringBuilder();
                strBdr.Append(type.ToString());
                strBdr.Append(" ");
                strBdr.Append(time.ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"));
                // strBdr.Append(time.ToLocalTime().ToString());
                strBdr.Append(" [t:");
                strBdr.Append(threadName);
                strBdr.Append("] ");
                strBdr.Append(funcName);
                strBdr.Append(":\r\n");
                strBdr.Append(msg);
                if (stackTraceStr != null && stackTraceStr.Length > 0)
                {
                    strBdr.Append("\r\n");
                    strBdr.Append(stackTraceStr);
                }
                return strBdr.ToString();
            }

        }

        /// <summary>
        /// 日志类型
        /// </summary>
        public enum LogType
        {
            DEBUG,
            INFO,
            WARN,
            ERROR
        }

        /// <summary>
        /// 日志处理接口
        /// </summary>
        public interface ILogHandler
        {
            /// <summary>
            /// 写入日志消息
            /// </summary>
            /// <param name="funcName"></param>
            /// <param name="stackTraceStr"></param>
            /// <param name="msg"></param>
            void WriteLog(LogType type, string funcName, string stackTraceStr, string msg);
        }

        /// <summary>
        /// 文本输出接口
        /// </summary>
        public class ConsoleLogHandler : ILogHandler
        {
            void ILogHandler.WriteLog(LogType type, string funcName, string stackTraceStr, string msg)
            {
                WriteConsoleLog(type, Logger.ToString(type, DateTime.Now, Thread.CurrentThread.ManagedThreadId.ToString(), funcName, stackTraceStr, msg));
            }

            /// <summary>
            /// 输出日志, 根据日志类型切换颜色.
            /// </summary>
            /// <param name="type"></param>
            /// <param name="logStr"></param>
            protected void WriteConsoleLog(LogType type, string logStr)
            {
                // 根据类型选择不一样的颜色
                ConsoleColor foregroundColor = Console.ForegroundColor;
                switch (type)
                {
                    case LogType.DEBUG:
                        foregroundColor = ConsoleColor.Green;
                        break;
                    case LogType.WARN:
                        foregroundColor = ConsoleColor.Yellow;
                        break;
                    case LogType.ERROR:
                        foregroundColor = ConsoleColor.Red;
                        break;
                }
                // 输出
                Console.ForegroundColor = foregroundColor;
                Console.WriteLine(logStr);
                Console.ResetColor();
            }

        }

        /// <summary>
        /// 文本输出接口
        /// </summary>
        public class FileLogHandler : ConsoleLogHandler, ILogHandler
        {
            protected string filePath; // 文件夹路径
            protected string prefix;   // 文件前缀

            public FileLogHandler(string filePath, string prefix)
            {
                this.filePath = filePath;
                this.prefix = prefix;

                // 路径创建
                string path = (!string.IsNullOrEmpty(filePath)) ? Path.GetDirectoryName(filePath) : null;
                if (!string.IsNullOrEmpty(path))
                {
                    Directory.CreateDirectory(path);
                }
            }

            void ILogHandler.WriteLog(LogType type, string funcName, string stackTraceStr, string msg)
            {
                // 生成文本
                string outStr = Logger.ToString(type, DateTime.Now, Thread.CurrentThread.ManagedThreadId.ToString(), funcName, stackTraceStr, msg);
                WriteConsoleLog(type, outStr);
#if TRACE
                // 根据类型输出到文本
                if (type >= LogType.ERROR)
                {
                    WriteFileLog(LogType.ERROR, outStr);
                }
                if (type >= LogType.WARN)
                {
                    WriteFileLog(LogType.WARN, outStr);
                }
                if (type >= LogType.INFO)
                {
                    WriteFileLog(LogType.INFO, outStr);
                }
#endif

#if DEBUG
                if (type >= LogType.DEBUG)
                {
                    WriteFileLog(LogType.DEBUG, outStr);
                }
#endif
            }

            /// <summary>
            /// 写入日志到文本
            /// </summary>
            /// <param name="type"></param>
            /// <param name="logStr"></param>
            protected void WriteFileLog(LogType type, string logStr)
            {
                // 根据类型处理文本
                StringBuilder outPath = new StringBuilder();
                outPath.Append(filePath);
                outPath.Append(Path.DirectorySeparatorChar);
                outPath.Append(prefix);
                switch (type)
                {
                    case LogType.DEBUG:
                        outPath.Append("_debug");
                        break;
                    case LogType.INFO:
                        outPath.Append("_info");
                        break;
                    case LogType.WARN:
                        outPath.Append("_warn");
                        break;
                    case LogType.ERROR:
                        outPath.Append("_error");
                        break;
                    default:
                        return;
                }
                // 日期拼接
                string dateStr = DateTime.Now.ToString("yyyyMMdd");
                outPath.Append(".");
                outPath.Append(dateStr);
                // 文件拼接
                outPath.Append(".log");

                try
                {
                    // 写入内容
                    File.AppendAllText(outPath.ToString(), logStr);
                    File.AppendAllText(outPath.ToString(), "\r\n");
                }
                catch (Exception e)
                {
                    WriteConsoleLog(LogType.ERROR, "写入日志文件错误! " + outPath + " by " + e.ToString());
                }
            }
        }
    }
}
